package com.chen.util;

import com.chen.config.JWTConfig;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.time.Instant;
import java.util.Date;

@Component
public class JwtUtil {

    private final Key key;
    private final long expireTime;


    // spring创建bean的时候,会先解析依赖, 扫描带有@Component, @ConfigurationProperties 的类,
    // spring先创建了JWTConfig, 并把配置文件里面的属性读了进来
    //在创建JwtUtil的时候发现需要JWTConfig, 就把已经准备好的JWTConfig传了进来
    public JwtUtil(JWTConfig jwtConfig) {
        // Base64 解码后生成 Key
        this.key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtConfig.getSecretKey()));
        this.expireTime = jwtConfig.getExpireTime();
    }


    /**
     * 生成密钥 每次执行这段代码都会生成一个不同的密钥，所以它只在开发阶段执行一次，
     * 拿到密钥字符串后，就可以直接写进配置文件：
     */
    public static void generateKey() {
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String secretString = Encoders.BASE64.encode(key.getEncoded());
        System.out.println("生成的密钥是:" + secretString);
    }

    /**
     * 生成token
     *
     * @return
     */
    public String generateToken(String userId) {

        Instant now = Instant.now();   //当前时间
        return Jwts.builder()
                // 设置payload标准字段
                .setSubject(userId) // 表示哪个用户的 Token
                .setIssuedAt(Date.from(now))  //签发时间
                .setExpiration(Date.from(now.plusMillis(this.expireTime))) //过期时间
                //设置自定义的payload
                // .claim("role", "user")   // 需要就添加自定义字段
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();
    }


    /**
     * 解析并验证 Token
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(key)     // 设置签名的 key
                    .build()
                    .parseClaimsJws(token)  // 会自动验证签名、过期时间,如果签名错误或过期，会直接抛异常
                    .getBody();             // 返回 payload 部分 (Claims)
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("token expired");
        } catch (JwtException e) {
            throw new RuntimeException("token invalid");
        }
    }


}